Test Failed
Push — main ( a6b018...b2d6b6 )
by Ehsan
02:40
created

Bot.toTweets   A

Complexity

Conditions 3

Size

Total Lines 16
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 14
dl 0
loc 16
ccs 7
cts 7
cp 1
rs 9.7
c 0
b 0
f 0
cc 3
crap 3
1 1
import Twit from 'twit';
2 1
import Debug from 'debug';
3 1
import Tweet, {TweetConfig} from './Tweet';
4 1
import Helper from './Helper';
5 1
import Constant from './Constant';
6
7
type BotConfig = {
8
    screenName: string,
9
    debugNamespace?: string,
10
    apiConfig: Twit.Options,
11
    tweetConfig: TweetConfig
12
};
13
14 1
export default class Bot {
15
    twit: Twit;
16
    config: BotConfig;
17
    debug: Debug.Debugger;
18
19
    constructor(config: BotConfig) {
20 14
        this.twit = new Twit(config.apiConfig);
21 14
        this.config = config;
22 14
        this.debug = Helper.objectExists(config.debugNamespace) ? Debug(config.debugNamespace) : Debug(Constant.DEBUGGER_DEFAULT_NAMESPACE);
23
    }
24
25
    async retweet(searchParams: Twit.Params): Promise<void> {
26 8
        const tweets = await this.search(searchParams);
27
28 6
        if (!Helper.objectExists(tweets) || tweets.length === 0) {
29 1
            this.debug('Found no tweets.');
30 1
            return;
31
        }
32
33 5
        this.debug(`Found ${tweets.length} tweet(s).`);
34
35 5
        const retweetedTweets = await this.searchRetweetedTweets();
36
37 4
        let count = 0;
38 4
        for (const tweet of tweets) {
39 5
            count++;
40
41 5
            if (!tweet.isReTweetable()) {
42 1
                this.debug(`${count}. Tweet with id: '${tweet.rawTweet.id_str}' is not retweetable because: ${tweet.retweetError}.`);
43 1
                continue;
44
            }
45
46 4
            this.debug(`${count}. Tweet with id: '${tweet.rawTweet.id_str}' may be retweeted!`);
47
48 4
            if (this.hasAlreadyReTweeted(retweetedTweets, tweet)) {
49 1
                continue;
50
            }
51
52 3
            this.debug(`Retweeting tweet with id: '${tweet.rawTweet.id_str}' ...`);
53 3
            const result = await this.twit.post('statuses/retweet/:id', {
54
                id: tweet.rawTweet.id_str
55
            });
56
57 3
            if (result.resp.statusCode === 200) {
58 2
                this.debug('Retweeted!');
59 2
                this.debug('Liking ...');
60
61 2
                const result = await this.twit.post('favorites/create', {
62
                    id: tweet.rawTweet.id_str
63
                });
64
65 2
                if (result.resp.statusCode === 200) {
66 1
                    this.debug('Liked!');
67
                } else {
68 1
                    this.debug('Cannot like.');
69 2
                    if (Helper.objectExists(result.resp.statusMessage)) {
70 1
                        this.debug(result.resp.statusMessage);
71
                    }
72
73 1
                    throw new Error('Cannot like');
74
                }
75
            } else {
76 1
                this.debug('Cannot retweet.');
77 2
                if (Helper.objectExists(result.resp.statusMessage)) {
78 1
                    this.debug(result.resp.statusMessage);
79
                }
80
81 1
                throw new Error('Cannot retweet');
82
            }
83
        }
84
85 2
        this.debug('All done.');
86
    }
87
88
    private async search(searchParams: Twit.Params): Promise<Tweet[]> {
89 8
        this.debug('Searching recent tweets with the following params ...');
90 8
        this.debug(searchParams);
91
92 8
        const result = await this.twit.get('search/tweets', searchParams);
93
94 7
        if (result.resp.statusCode === 200 && Helper.objectExists(result.data)) {
95 6
            this.debug('Search successfully completed!');
96
97 6
            const statuses = (result.data as Twit.Twitter.SearchResults).statuses;
98
99 6
            return this.toTweets(statuses);
100
        } else {
101 1
            this.debug('Cannot search recent tweets.');
102 2
            if (Helper.objectExists(result.resp.statusMessage)) {
103 1
                this.debug(result.resp.statusMessage);
104
            }
105
106 1
            throw new Error('Cannot search recent tweets');
107
        }
108
    }
109
110
    private async searchRetweetedTweets(): Promise<Tweet[]> {
111 5
        this.debug(`Searching recent retweets by user: '${this.config.screenName}' ...`);
112
113 5
        const result = await this.twit.get('statuses/user_timeline', {
114
            screen_name: this.config.screenName,
115
            // max allowed is 200
116
            count: 200,
117
            exclude_replies: true,
118
            include_rts: true
119
        });
120
121 5
        if (result.resp.statusCode === 200 && Helper.objectExists(result.data)) {
122 4
            const recentTweets = result.data as Twit.Twitter.Status[];
123
124 4
            const retweets = this.toTweets(recentTweets, true);
125 4
            this.debug(`Found '${retweets.length}' retweet(s).`);
126
127 4
            return retweets;
128
        } else {
129 1
            this.debug('Cannot search recent retweets.');
130 2
            if (Helper.objectExists(result.resp.statusMessage)) {
131 1
                this.debug(result.resp.statusMessage);
132
            }
133
134 1
            throw new Error('Cannot search recent retweets');
135
        }
136
    }
137
138
    private toTweets(statuses: Twit.Twitter.Status[], onlyRetweets = false): Tweet[] {
139 10
        const tweets: Tweet[] = [];
140
141
        let tweet: Tweet;
142 10
        for (const status of statuses) {
143 8
            tweet = new Tweet(status, this.config.tweetConfig);
144
145 8
            if (onlyRetweets && !tweet.isRetweet()) {
146 1
                continue;
147
            }
148
149 7
            tweets.push(tweet);
150
        }
151
152 10
        return tweets;
153
    }
154
155
    private hasAlreadyReTweeted(retweetedTweets: Tweet[], tweet: Tweet): boolean {
156 4
        this.debug(`Checking to see if tweet with id: '${tweet.rawTweet.id_str}' has been already retweeted ...`);
157
158 4
        for (const recentTweet of retweetedTweets) {
159 4
            if (recentTweet.rawTweet.retweeted_status && recentTweet.rawTweet.retweeted_status.id_str === tweet.rawTweet.id_str) {
160 1
                this.debug(`Tweet with id: '${tweet.rawTweet.id_str}' has been already tweeted.`);
161 1
                return true;
162
            }
163
        }
164
165 3
        this.debug(`Tweet with id: '${tweet.rawTweet.id_str}' has NOT been tweeted.`);
166
167 3
        return false;
168
    }
169
}